package com.kaigejava.rulesengine.bussiness.service.impl;

import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.kaigejava.rulesengine.bussiness.mapper.CreEngineMapper;
import com.kaigejava.rulesengine.bussiness.service.IEngineService;
import com.kaigejava.rulesengine.engine.core.api.MessageCacheType;
import com.kaigejava.rulesengine.engine.core.api.RulesChangeType;
import com.kaigejava.rulesengine.engine.core.api.RulesEngine;
import com.kaigejava.rulesengine.engine.core.api.RulesEngineApiResponse;
import com.kaigejava.rulesengine.engine.core.cache.MessageCache;
import com.kaigejava.rulesengine.engine.core.cache.RulesChangeDataRunnable;
import com.kaigejava.rulesengine.engine.core.kie.KieResponse;
import com.kaigejava.rulesengine.engine.core.kie.RulesMapper;
import com.kaigejava.rulesengine.engine.core.typedefine.DataEntryPoint;
import com.kaigejava.rulesengine.engine.core.utils.ProducerMessageBuilder;
import com.kaigejava.rulesengine.engine.dto.CreRuleDto;
import com.kaigejava.rulesengine.engine.dto.RuleHitDto;
import com.kaigejava.rulesengine.engine.fact.EngineResponse;
import com.kaigejava.rulesengine.engine.fact.RecyclingFact;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author kaigejava
 */
@Service("creEngineService")
public class CreEngineServiceImpl implements IEngineService {
    public static final String CALCULATE_POINT = "_calculatePoint";
    public static final String LINE = "_line";
    private static Logger logger = LoggerFactory.getLogger(IEngineService.class);
    private static final int CACHE_TIME = 60 * 10;
    private static final int MAX_THREADS = Runtime.getRuntime().availableProcessors();
    private final static int QUEUE_CAPACITY = 5000;
    private final static String RULE_POOL_NAME = "rulePool";

    private ThreadPoolExecutor executor = new ThreadPoolExecutor(MAX_THREADS, MAX_THREADS * 5,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(QUEUE_CAPACITY), new NamedThreadFactory(RULE_POOL_NAME));

    @Autowired
    private CreEngineMapper creEngineMapper;



    @Override
    public List<CreRuleDto> findCreRuleList(Map<String, Object> map) {
        List<CreRuleDto> taskList = creEngineMapper.findCreRuleList(map);
        return taskList;
    }

    /**
     * 启动规则引擎
     **/
    @PostConstruct
    private void run() {
        if (RulesEngine.start() == RulesEngineApiResponse.OK) {
            //load rules
            Map<String, Object> params = new HashMap<String, Object>(200);
            List<CreRuleDto> creRuleList = findCreRuleList(params);
            if (CollectionUtils.isNotEmpty(creRuleList)) {
                if (RulesEngine.loadRules(creRuleList) == RulesEngineApiResponse.OK) {
                    logger.warn("Loaded rules ,init RulesEngine ok.");
                } else {
                    logger.warn("Loaded rules ,init RulesEngine Faile.");
                }
            }
        }

        executor.execute(new RulesChangeDataRunnable(MessageCache.getDataCache()));
    }

    /**
     * 引擎启动
     *
     * @param data
     * @return
     */
    private EngineResponse<Object> execute(RecyclingFact data) {
        KieResponse kieResponse = new KieResponse();
        EngineResponse<Object> engineResponse = new EngineResponse<Object>();
        try {
            RulesEngineApiResponse rulesEngineApiResponse = RulesEngine.fireRules(data, kieResponse, DataEntryPoint.DUBBO_POINT);
            if (rulesEngineApiResponse.equals(RulesEngineApiResponse.RULES_ENGINE_NO_START)) {
                engineResponse.setCode(501L);
                engineResponse.setMessage("规则引擎runtime没有启动");
            }
            if (rulesEngineApiResponse.equals(RulesEngineApiResponse.KIE_RUNTIME_BUSY)) {
                engineResponse.setCode(501L);
                engineResponse.setMessage("规则引擎runtime 繁忙");
            }
            logger.warn(">>>Engine start code: {}, message: {}, ruleId", engineResponse.getCode(), engineResponse.getMessage(), data.getRuleId());
            long kieCode = kieResponse.getCode();
            engineResponse.setCode(kieCode);
            engineResponse.setMessage(kieResponse.getMessage());
            engineResponse.setData(kieResponse.getResult());
        } catch (Exception exception) {
            logger.error("executeEngineStartException, message: {}", exception);
        }
        return engineResponse;
    }

    @Override
    public RuleHitDto isHitRule(RecyclingFact data) {
        EngineResponse<Object> engineResponse = execute(data);
        if (engineResponse.getCode().equals(0L)) {
            RuleHitDto ruleHitDto = (RuleHitDto) engineResponse.getData();
            return ruleHitDto;
        }
        return null;
    }

    /**
     * 动态添加规则
     *
     * @param rule
     */
    @Override
    public void bufferSaveRule(CreRuleDto rule) {
        MessageCache.getDataCache().putBufferData(
                ProducerMessageBuilder.buildMessage(rule, RulesChangeType.ADD), MessageCacheType.changes.getType());
    }

    /**
     * 动态修改规则
     *
     * @param rule
     */
    @Override
    public void bufferUpdateRule(CreRuleDto rule) {
        MessageCache.getDataCache().putBufferData(
                ProducerMessageBuilder.buildMessage(rule, RulesChangeType.UPDATE), MessageCacheType.changes.getType());
    }

    /**
     * 动态删除规则
     *
     * @param ruleId
     */
    @Override
    public void bufferDeleteRule(Integer ruleId) {
        CreRuleDto rule = new CreRuleDto();
        rule.setRuleId(ruleId);
        MessageCache.getDataCache().putBufferData(
                ProducerMessageBuilder.buildMessage(rule, RulesChangeType.DELETE), MessageCacheType.changes.getType());
    }

    /**
     * 积分计算
     *
     * @param pathId
     * @param actionId
     * @param value
     * @param hour
     * @return
     */
    @Override
    public Map<String, Object> calculatePoint(int pathId, int actionId, int value, int hour) {
        Map<String, Object> map = new HashedMap();
        map.put("pathId", pathId);
        map.put("actionId", actionId);
        List<RuleHitDto> hitRuleList = new CopyOnWriteArrayList<RuleHitDto>();
        List<RuleHitDto> nohitRuleList = new CopyOnWriteArrayList<RuleHitDto>();
        String key = IEngineService.class.getName() + CALCULATE_POINT + pathId + LINE + actionId;
        List<CreRuleDto> taskRuleDtos = creEngineMapper.getTaskRuleByPathAndActionId(map);
        if (CollectionUtils.isNotEmpty(taskRuleDtos)) {
            for (CreRuleDto ruleDto : taskRuleDtos) {
                RecyclingFact recyclingFact = new RecyclingFact();
                recyclingFact.setRuleId(ruleDto.getRuleId());
                recyclingFact.setValue(value);
                recyclingFact.setHours(hour);
                EngineResponse<Object> engineResponse = execute(recyclingFact);
                if (engineResponse.getCode().equals(0L)) {
                    RuleHitDto ruleHitDto = (RuleHitDto) engineResponse.getData();
                    hitRuleList.add(ruleHitDto);
                } else {
                    RuleHitDto ruleHitDto = new RuleHitDto();
                    ruleHitDto.setRuleId(ruleDto.getRuleId());
                    nohitRuleList.add(ruleHitDto);
                }
            }
        }
        map.put("hitRuleList", hitRuleList);
        map.put("noHitRuleList", hitRuleList);
        return map;
    }

    @Override
    public void deleteTaskRuleCache(int pathId, int actionId) {
        String key = IEngineService.class.getName() + CALCULATE_POINT + pathId + LINE + actionId;

    }

    @Override
    public String findDroiksRuleMap() {
        return RulesMapper.getRulesJson();
    }

}
